package base;

import values.Array;
import values.Value;
import values.Integer;
import values.Boolean;
import values.FantaString;
import instructions.BadInstructionException;
import instructions.Instruction;
import expressions.BadExpressionException;
import expressions.Expression;

public class Interpreter {

	/**
	 * Evaluates the instruction 'instruction' with a new a new empty
	 * environment
	 * 
	 * @param instruction
	 * @return the environment after the execution of the instruction
	 */

	public static Environment instr(Instruction instruction) {
		return instr(instruction, new Environment());
	}

	/**
	 * Evaluates the instruction 'instruction' with the environment 'env'
	 * 
	 * @param instruction
	 * @return the environment after the execution of the instruction
	 */

	public static Environment instr(Instruction instruction, Environment env) {

		Environment newEnv = null;

		try {
			newEnv = instruction.instr(env);
		} catch (BadInstructionException e) {
			System.err.println(e.getMessage());
			System.exit(1);
		}

		return newEnv;
	}

	/**
	 * Returns ast as an Integer if it's an Integer. If it's not, it evaluates
	 * the ast and returns the corresponding Integer. If it's not an Integer,
	 * the method throws a BadExpressionException.
	 * 
	 * @param ast
	 * @param env
	 * @return the Integer that corresponds to ast.
	 * @throws BadExpressionException
	 */

	public static Integer getInt(AST ast, Environment env)
			throws BadExpressionException {

		if (ast instanceof Integer)
			return (Integer) ast;

		if (ast instanceof Expression) {
			Value temp = ((Expression) ast).eval(env);
			if (temp instanceof Integer)
				return (Integer) temp;
		}

		throw new BadExpressionException("AST is not an integer.");

	}

	/**
	 * Returns ast as an Boolean if it's a Boolean. If it's not, it evaluates
	 * the ast and returns the corresponding Boolean. If it's not a Boolean, the
	 * method throws a BadExpressionException.
	 * 
	 * @param ast
	 * @param env
	 * @return the Boolean that corresponds to ast.
	 * @throws BadExpressionException
	 */

	public static Boolean getBoolean(AST ast, Environment env)
			throws BadExpressionException {

		if (ast instanceof Boolean)
			return (Boolean) ast;

		if (ast instanceof Expression) {
			Value temp = ((Expression) ast).eval(env);
			if (temp instanceof Boolean)
				return (Boolean) temp;
		}

		throw new BadExpressionException("AST is not a boolean.");

	}

	/**
	 * Returns ast as an Array if it's an Array. If it's not, it evaluates the
	 * ast and returns the corresponding Array. If it's not an Array, the method
	 * throws a BadExpressionException.
	 * 
	 * @param ast
	 * @param env
	 * @return the Array that corresponds to ast.
	 * @throws BadExpressionException
	 */

	public static Array getArray(AST ast, Environment env)
			throws BadExpressionException {

		if (ast instanceof Array)
			return (Array) ast;

		if (ast instanceof Expression) {
			Value temp = ((Expression) ast).eval(env);
			if (temp instanceof Array)
				return (Array) temp;
		}

		throw new BadExpressionException("AST is not an array.");

	}

	/**
	 * Returns ast as a FantaString if it's a FantaString. If it's not, it
	 * evaluates the ast and returns the corresponding FantaString. If it's not
	 * a FantaString, the method throws a BadExpressionException.
	 * 
	 * @param ast
	 * @param env
	 * @return the FantaString that corresponds to ast.
	 * @throws BadExpressionException
	 */

	public static FantaString getString(AST ast, Environment env)
			throws BadExpressionException {

		if (ast instanceof FantaString)
			return (FantaString) ast;

		if (ast instanceof Expression) {
			Value temp = ((Expression) ast).eval(env);
			if (temp instanceof FantaString)
				return (FantaString) temp;
		}

		throw new BadExpressionException("AST is not a string.");

	}
}
